Goals

we’ll do these by taking a close look at some interesting data: Airbnb listings for New York City in May 2017.

It’s Okay if you can’t start using these techniques right away after the workshop. To be comfortable with a tool like R takes some time and practice. But we do hope that your exposure today to these tools and techniques will open your eyes about possibilities and motivate you to keep learning.

RStudio

RStudio is a free program that makes writing R code much more enjoyable and efficient.

It has four main panes. This one is the code editor (also known as script editor and source editor). This is where we’ll spend most of our time. We’ll say more about the other panes when the time comes.

R Notebook

Before starting the analysis, let’s understand how this document works. I’m assuming you’re reading this on RStudio.

This is an R Notebook. An R Notebook contains commentary interspersed with code chunks. The code chunks can be run (executed) independently and interactively. The output will appear below the code chunk. R Notebooks are easy to convert to well-formatted final documents as a pdf file, a webpage, or an MS Word file. More here

Click on the preview button above to get an idea.

What you’re reading is the commentary and what you see below is the container for a code chunk.

# The code goes here

To run a code chunk, click on the little triangle at the right edge of the code chunk. Or, use the keyboard shortcut CTRL + SHIFT + ENTER.

Exercise: Run the chunk below and see what happens.

a <- 5 + 7
print(a)
[1] 12

As you see, the output appears under the code. This way you can immediately see the result of the code you write at all steps of the analysis.

(Do you know what’s going on in the code above? We’ll talk about variables and assignments later.)

Feel free to add your commentary and code anywhere in this document. You can always download the unmodified version from here.

To write your own code chunk, look for the insert button above and then select R. Or, use the keyboard shortcut CTRL + ALT + I.

Exercise: Place the cursor below and create a container for writing code. Now write some code and run it. For example, find the result of 3543 / 562.

The data

The dataset contains almost all the listings in NYC in May 2017.The data came from Inside Airbnb.

It’s always a good idea to approach a new dataset with a few basic questions. Some examples:

Here’s some background information that may answer some of these questions.

Let’s take a look at the data. Open the csv file in Excel. Browse around a bit. Any observations or questions?

Discuss: Can you explain what each column is about?

Discuss: Are there any data that you’d like to have but not there?

Discuss: What’s the first thing you’d like to find out from these data?

Discuss: What do you think about the quality of the data? Why?

The context

Data analysis happens within a larger context. Usually there’s an overarching business, policy, or scientific question that one would like to answer. Often that question is not very clear. Whatever the case, you need to approach the data with curiosity about the larger context.

The more you understand the context of the data, the better will be your questions and hypotheses guiding your analysis.

For our dataset, we should have a good understanding of the business model of Airbnb as well as the economy and geography of NYC.

Discuss: Is there anything else we should know about?

Airbnb

Discuss: How much do we need to know about Airbnb and its business? Is my personal experience with Airbnb enough? What if I don’t have any personal experience?

How Airbnb works

Example airbnb listing

New York City

We can start with a map of the city.

img

img

The map shows the relative size and location of the five boroughs of NYC.

Discuss: What else do we know about these boroughs? What about the neighborhoods within the boroughs?

Analysis

Install and load libraries

We’ll first install a few libraries that we’ll need at different stages of the analysis.

# For loading data
install.packages("readr")
Installing package into 㤼㸱C:/Users/mehedia/Documents/R/win-library/3.4㤼㸲
(as 㤼㸱lib㤼㸲 is unspecified)
trying URL 'https://mran.microsoft.com/snapshot/2017-05-01/bin/windows/contrib/3.4/readr_1.1.0.zip'
Content type 'application/zip' length 1260805 bytes (1.2 MB)
downloaded 1.2 MB
package ‘readr’ successfully unpacked and MD5 sums checked

The downloaded binary packages are in
    C:\Users\mehedia\AppData\Local\Temp\Rtmpi0GdVN\downloaded_packages
# For data manipulation (we'll spend most time with this one)
install.packages("dplyr")
Installing package into 㤼㸱C:/Users/mehedia/Documents/R/win-library/3.4㤼㸲
(as 㤼㸱lib㤼㸲 is unspecified)
trying URL 'https://mran.microsoft.com/snapshot/2017-05-01/bin/windows/contrib/3.4/dplyr_0.5.0.zip'
Content type 'application/zip' length 2555450 bytes (2.4 MB)
downloaded 2.4 MB
package ‘dplyr’ successfully unpacked and MD5 sums checked

The downloaded binary packages are in
    C:\Users\mehedia\AppData\Local\Temp\Rtmpi0GdVN\downloaded_packages
# For visualization
install.packages("ggplot2")
Installing package into 㤼㸱C:/Users/mehedia/Documents/R/win-library/3.4㤼㸲
(as 㤼㸱lib㤼㸲 is unspecified)
trying URL 'https://mran.microsoft.com/snapshot/2017-05-01/bin/windows/contrib/3.4/ggplot2_2.2.1.zip'
Content type 'application/zip' length 2781570 bytes (2.7 MB)
downloaded 2.7 MB
package ‘ggplot2’ successfully unpacked and MD5 sums checked

The downloaded binary packages are in
    C:\Users\mehedia\AppData\Local\Temp\Rtmpi0GdVN\downloaded_packages
# For string manipulation
install.packages("stringr")
Installing package into 㤼㸱C:/Users/mehedia/Documents/R/win-library/3.4㤼㸲
(as 㤼㸱lib㤼㸲 is unspecified)
trying URL 'https://mran.microsoft.com/snapshot/2017-05-01/bin/windows/contrib/3.4/stringr_1.2.0.zip'
Content type 'application/zip' length 148776 bytes (145 KB)
downloaded 145 KB
package ‘stringr’ successfully unpacked and MD5 sums checked

The downloaded binary packages are in
    C:\Users\mehedia\AppData\Local\Temp\Rtmpi0GdVN\downloaded_packages
# To work with date and time
install.packages("lubridate")
Installing package into 㤼㸱C:/Users/mehedia/Documents/R/win-library/3.4㤼㸲
(as 㤼㸱lib㤼㸲 is unspecified)
trying URL 'https://mran.microsoft.com/snapshot/2017-05-01/bin/windows/contrib/3.4/lubridate_1.6.0.zip'
Content type 'application/zip' length 667329 bytes (651 KB)
downloaded 651 KB
package ‘lubridate’ successfully unpacked and MD5 sums checked

The downloaded binary packages are in
    C:\Users\mehedia\AppData\Local\Temp\Rtmpi0GdVN\downloaded_packages

And then we load the libraries for our current R session. After these libraries are successfully loaded, all the functions in these libraries will be available for our use.

library(readr)
library(dplyr)

Attaching package: 㤼㸱dplyr㤼㸲

The following objects are masked from 㤼㸱package:stats㤼㸲:

    filter, lag

The following objects are masked from 㤼㸱package:base㤼㸲:

    intersect, setdiff, setequal, union
library(ggplot2)
library(stringr)
library(lubridate)

Attaching package: 㤼㸱lubridate㤼㸲

The following object is masked from 㤼㸱package:base㤼㸲:

    date

Load and prepare data

We’ll use the function read_csv, which comes from the library readr, to load data from file and convert the data into a dataframe. A dataframe is a tabular data structure, with columns as variables and rows as observations. In this case, exactly as it is in the csv file.

Discuss: What’a csv file? What other formats are out there for storing data? How can we load data from an unfamiliar format?

df <- read_csv("airbnb_newyork.csv")
Parsed with column specification:
cols(
  .default = col_integer(),
  host_since = col_character(),
  host_response_time = col_character(),
  host_response_rate = col_character(),
  neighbourhood = col_character(),
  borough = col_character(),
  property_type = col_character(),
  room_type = col_character(),
  bathrooms = col_double(),
  amenities = col_character(),
  price = col_number(),
  calendar_updated = col_character(),
  cancellation_policy = col_character(),
  listing_url = col_character(),
  description = col_character()
)
See spec(...) for full column specifications.

First thing to check if the data have been successully loaded and assigned to the variable (here df). We don’t see any error — which is a good sign. We can also see the variable df in the environment pane (usually to the right of this code editor pane – click on “Environment” if hidden).

Next thing to check is whether read_csv was able to correctly infer the data type of each variable.

The function glimpse will give us a better glimpse.

glimpse(df)
Observations: 40,752
Variables: 28
$ host_id                     <int> 58306608, 124280354, 124280354, 124280354, 124280354, 1242...
$ host_since                  <chr> "2/11/2016", "4/4/2017", "4/4/2017", "4/4/2017", "4/4/2017...
$ host_response_time          <chr> "within an hour", "within an hour", "within an hour", "wit...
$ host_response_rate          <chr> "0.78", "0.99", "0.99", "0.99", "0.99", "0.99", "0.95", "N...
$ host_listings_count         <int> 1, 7, 7, 7, 7, 7, 1, 1, 1, 7, 1, 1, 1, 1, 1, 1, 2, 2, 1, 1...
$ neighbourhood               <chr> "Hell's Kitchen", "Hell's Kitchen", "Hell's Kitchen", "Hel...
$ borough                     <chr> "Manhattan", "Manhattan", "Manhattan", "Manhattan", "Manha...
$ zip_code                    <int> 10000, 10001, 10001, 10001, 10001, 10001, 10001, 10001, 10...
$ property_type               <chr> "Apartment", "Apartment", "Apartment", "Apartment", "Apart...
$ room_type                   <chr> "Private room", "Shared room", "Shared room", "Shared room...
$ accommodates                <int> 2, 1, 1, 1, 1, 1, 2, 1, 1, 1, 2, 2, 1, 2, 1, 1, 2, 2, 1, 1...
$ bathrooms                   <dbl> 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.5, 3.0, 1.0, 1.0, 1.0, 1.0...
$ bedrooms                    <int> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1...
$ beds                        <int> 1, 1, 1, 1, 1, 1, 2, 6, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1...
$ amenities                   <chr> "{TV,Internet,\"Wireless Internet\",\"Air conditioning\",P...
$ price                       <dbl> 200, 28, 39, 39, 39, 43, 50, 50, 50, 53, 54, 55, 55, 60, 6...
$ calendar_updated            <chr> "never", "5 days ago", "5 days ago", "3 days ago", "6 days...
$ number_of_reviews           <int> 0, 0, 2, 1, 0, 3, 185, 0, 0, 0, 21, 5, 3, 12, 0, 20, 1, 1,...
$ review_scores_rating        <int> NA, NA, 100, 100, NA, 87, 93, NA, NA, NA, 96, 100, 80, 92,...
$ review_scores_accuracy      <int> NA, NA, 10, 10, NA, 9, 9, NA, NA, NA, 10, 10, 10, 9, NA, 1...
$ review_scores_cleanliness   <int> NA, NA, 10, 10, NA, 8, 9, NA, NA, NA, 10, 8, 8, 9, NA, 9, ...
$ review_scores_checkin       <int> NA, NA, 10, 10, NA, 9, 10, NA, NA, NA, 10, 10, 10, 9, NA, ...
$ review_scores_communication <int> NA, NA, 10, 10, NA, 9, 10, NA, NA, NA, 10, 10, 10, 9, NA, ...
$ review_scores_location      <int> NA, NA, 10, 10, NA, 9, 10, NA, NA, NA, 10, 10, 9, 10, NA, ...
$ review_scores_value         <int> NA, NA, 10, 10, NA, 9, 10, NA, NA, NA, 10, 10, 8, 9, NA, 1...
$ cancellation_policy         <chr> "moderate", "flexible", "flexible", "moderate", "flexible"...
$ listing_url                 <chr> "https://www.airbnb.com/rooms/18292044", "https://www.airb...
$ description                 <chr> "NBA player and China star Huge residence apartment, Hugel...

What you see within the angular brackets <...> next to each column name is the data type of that variable.

But what are data types? And, why are they important? What exactly is a variable in programming? What is a function?

Let’s take a detour.

Detour: variables, data types, and functions in R

Variables (programming)

In programming, a variable stores some value (not to be confused with what we call variable in statistics). Our code can then reference the name of the variable for different purposes.

We use the symbol <- (less than sign followed by hyphen) to assign value to a variable name. For example, in the code below, foo and bar are variable names. We assigned the values 43 and "kitten" to foo and bar respectively. You can read them as foo gets 43 and bar gets "kitten".

foo <- 43
bar <- "kitten"
print(foo * 10)
[1] 430

Important: I’ve been using the word variable to mean two different things. A variable in programming is what I described above. A variable in statistics is an attribute of something, whatever the data is about. For example, if the data is about people, some variables could be age, sex, income, address. The names of the columns of a dataframe are variables in the statistical sense. Usually, the context would clarify which meaning is intended.

Tip: Give short descriptive names to your variables. This will make the code easy to follow.

Exercise: a room is 11 yards long and 7.5 yards wide. Assign these values to width and length variables and then calculate the area of the room. You can, of course, give different names to these variables if you wish.

Data types

To understand how R stores and handles information, we need to know a little about data types.

But, first, vector: one of the key concepts in R. Think of a vector as simply a sequence of data. For example, a column in a data table is a vector. This is how you create a vector: c(element1, element2, element3, ...)

vec <- c(3, 5, 10, 20)
print(vec)
[1]  3  5 10 20

A vector can contain four main types of data:

  • Integer: such as 2, 543, 90.
  • Double: numbers other than integers, such as 4.56, 1/33. Doubles are always approximations.
  • Character: a sequence of letters, numbers, punctuations, etc., such as “rainbow”, “34265”. Character data are always written within quotes, either single or double (we’ll always use double for consistency).
  • Logical: data that can take on one of only two values — TRUE or FALSE.

Discuss: Why is “34265” of character type? What is a real world example of this kind of data?

There are two other important ways to store data: - factor, for categorical variables (here “variable” in statistical sense). A categorical variable is one that can take on a limited fixed number of values. - date-time or just date

Functions

The concept of a function is exactly the same as in Excel. You give some values (called parameters) to a function. The function does something with the values and returns a new value.

Here’s a simple function that we named add10. All it does is adds 10 to any number given to it.

add10 <-  function(num){
  new_value <-  num + 10
  return (new_value)
}

And, then, we can use this function whenever we need to add 10 to a number (Of course, we won’t. There are easier ways to add a number).

# Add 10 to 35
add10(35)
[1] 45

Today, we won’t write any function, Rather, we’ll use functions written by others. Here’s an example: the mean function, which comes preloaded in R.

# First let's create a vector
vec2 <- c(23, 53, 11, 34, 87, 100, 5, 12, 66, 9, 87, 110, 20, 33, 54, 43, 76)
# Let's calculate the mean of the vector
calculated_mean <- mean(vec2)
# Then output it with some text description
message("The mean of the vector vec2 is ", mean(calculated_mean))
The mean of the vector vec2 is 48.4117647058824

*Exercise: calculate the sum, median, and standard deviation (hint: sd) of the vector vec2.

Now that we understand data types, let’s load some new data.

Exercise: You’ll see there’s another data file in the folder: “us_babynames.csv”. Load the data from this file to a dataframe (hint: read_csv). Assign the dataframe to a variable named df_babynames. We’ll come back to this dataframe later.

Exercise: Now take a look at the dataframe’s data types. (hint: glimpse)

Back to analysis

Fix data types

Let’s take another look at the data types of our dataframe.

glimpse(df)
Observations: 40,752
Variables: 28
$ host_id                     <int> 58306608, 124280354, 124280354, 124280354, 124280354, 1242...
$ host_since                  <chr> "2/11/2016", "4/4/2017", "4/4/2017", "4/4/2017", "4/4/2017...
$ host_response_time          <chr> "within an hour", "within an hour", "within an hour", "wit...
$ host_response_rate          <chr> "0.78", "0.99", "0.99", "0.99", "0.99", "0.99", "0.95", "N...
$ host_listings_count         <int> 1, 7, 7, 7, 7, 7, 1, 1, 1, 7, 1, 1, 1, 1, 1, 1, 2, 2, 1, 1...
$ neighbourhood               <chr> "Hell's Kitchen", "Hell's Kitchen", "Hell's Kitchen", "Hel...
$ borough                     <chr> "Manhattan", "Manhattan", "Manhattan", "Manhattan", "Manha...
$ zip_code                    <int> 10000, 10001, 10001, 10001, 10001, 10001, 10001, 10001, 10...
$ property_type               <chr> "Apartment", "Apartment", "Apartment", "Apartment", "Apart...
$ room_type                   <chr> "Private room", "Shared room", "Shared room", "Shared room...
$ accommodates                <int> 2, 1, 1, 1, 1, 1, 2, 1, 1, 1, 2, 2, 1, 2, 1, 1, 2, 2, 1, 1...
$ bathrooms                   <dbl> 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.5, 3.0, 1.0, 1.0, 1.0, 1.0...
$ bedrooms                    <int> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1...
$ beds                        <int> 1, 1, 1, 1, 1, 1, 2, 6, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1...
$ amenities                   <chr> "{TV,Internet,\"Wireless Internet\",\"Air conditioning\",P...
$ price                       <dbl> 200, 28, 39, 39, 39, 43, 50, 50, 50, 53, 54, 55, 55, 60, 6...
$ calendar_updated            <chr> "never", "5 days ago", "5 days ago", "3 days ago", "6 days...
$ number_of_reviews           <int> 0, 0, 2, 1, 0, 3, 185, 0, 0, 0, 21, 5, 3, 12, 0, 20, 1, 1,...
$ review_scores_rating        <int> NA, NA, 100, 100, NA, 87, 93, NA, NA, NA, 96, 100, 80, 92,...
$ review_scores_accuracy      <int> NA, NA, 10, 10, NA, 9, 9, NA, NA, NA, 10, 10, 10, 9, NA, 1...
$ review_scores_cleanliness   <int> NA, NA, 10, 10, NA, 8, 9, NA, NA, NA, 10, 8, 8, 9, NA, 9, ...
$ review_scores_checkin       <int> NA, NA, 10, 10, NA, 9, 10, NA, NA, NA, 10, 10, 10, 9, NA, ...
$ review_scores_communication <int> NA, NA, 10, 10, NA, 9, 10, NA, NA, NA, 10, 10, 10, 9, NA, ...
$ review_scores_location      <int> NA, NA, 10, 10, NA, 9, 10, NA, NA, NA, 10, 10, 9, 10, NA, ...
$ review_scores_value         <int> NA, NA, 10, 10, NA, 9, 10, NA, NA, NA, 10, 10, 8, 9, NA, 1...
$ cancellation_policy         <chr> "moderate", "flexible", "flexible", "moderate", "flexible"...
$ listing_url                 <chr> "https://www.airbnb.com/rooms/18292044", "https://www.airb...
$ description                 <chr> "NBA player and China star Huge residence apartment, Hugel...

Some of the data types don’t look right. Let’s correct them.

Discuss: why is it important to have the correct data type?

But, first, let’s learn how to reference a column in a dataframe: the name of the dataframe followed by $ and then the column name. For example: df$cancellation_policy or df_babynames$Gender.

To change data type, we apply the relevant function, such as.character or as.factor, to a column and then assign the output of the function (values with changed data type) to the same column.

df$host_id <- as.character(df$host_id)
df$host_response_rate <- as.double(df$host_response_rate)
NAs introduced by coercion
df$property_type <- as.factor(df$property_type)
df$host_since <- mdy(df$host_since)

Exercise: Change the data type of any other variables you think necessary.

Missing values

Some values will almost inevitably be missing in a medium to large dataset. There are a few options for dealing with missing values.

  • We can drop all rows that have one or more missing values.
  • Drop rows that have missing values in particular columns.
  • Replace the missing values with some other value.
  • Do nothing, but do remember to take care of them when running arithmetic operations. (explained later)

In our case, we’ll go with the last option.

Discuss: Is the last option the best for our dataset? What would be a better alternative?

Data manipluation and exploration

Finally, we’re ready to dive into the data. We’ll use six functions — think of them as six verbs — to slice and dice the data in all kinds of ways. These functions come from the dplyr library. These are:

  • filter
  • select
  • mutate
  • arrange
  • summarize
  • group_by

We’ll first learn these functions one by one and later we’ll learn how to combine them for powerful analysis.

For each of these functions, we provide the name of the dataframe as the first parameter. The subsequent parameters inform what the function is to do with the dataframe.

filter

You use filter, when you want a subset of the rows based on one or more conditions. The returned rows will be those that meet the condition(s).

The syntax is: filter(name_of_dataframe, condition1, condition2, ...)

Use these logical operators to create conditions:

< less than <= less than or equal to > greater than >= greater than or equal to == exactly equal to != not equal to !x Not x x | y x OR y x & y x AND y

Example: Return a dataframe with only those rows where the neighbourhood is Chelsea.

filter(df, neighbourhood == "Chelsea")

Example: Return a dataframe with only those rows where the price is more than 5000.

You can combine multiple conditions.

Example: Return a dataframe with rows where borough is Manhattan and price is less than 50.

Example: Return a dataframe with rows where cancellation_policy is flexible or accommodates more than 2.

Example: Return a dataframe with rows where number_of_reviews is not 0

filter(df, number_of_reviews != 0)

Note: when multiple conditions are separated by commas are assumed to have and logical operator. Using & instead would be the same thing. For or and other logical operators, we have to explicitly use the appropriate logical operator (e.g. | for or).

Exercise: Return a dataframe with rows where number of bedrooms is not 1 and property_type is House

Exercise: Return a dataframe with rows where host_response_time is within an hour or host_response_rate is more than 90% or calendar was updated today

select

Our second verb is select, which is used to return a subset of the columns.

The syntax is: select(name_of_dataframe, name_of_column1, name_of_column2, ...)

Example: Return a dataframe with only the beds column

Example: Return a dataframe with columns host_response_time, cancellation_policy, and neighbourhood


Here are some excellent resources if you want to keep learning R:

LS0tDQp0aXRsZTogIkEgU3VydmV5IG9mIE5ldyBZb3JrIENpdHkncyBBaXJibmIgTGlzdGluZ3MiDQpzdWJ0aXRsZTogJ1IgZm9yIGJlZ2lubmVyczogYSBkYXRhIGFuYWx5c2lzIHdvcmtzaG9wJw0KYXV0aG9yOiAiQXNpZiBNZWhlZGkiDQpkYXRlOiAnYHIgU3lzLkRhdGUoKWAnDQpvdXRwdXQ6DQogIGh0bWxfbm90ZWJvb2s6DQogICAgdG9jOiB0cnVlDQogICAgdGhlbWU6IGZsYXRseQ0KICAgIGhpZ2hsaWdodDogaGFkZG9jaw0KLS0tDQoNCiMgR29hbHMNCg0KLSBMZWFybiB0byB0aGluayBsaWtlIGEgZGF0YSBhbmFseXN0DQotIExlYXJuIHRoZSBiYXNpY3Mgb2YgUiBwcm9ncmFtbWluZyBsYW5ndWFnZQ0KLSBMZWFybiBzb21lIHBvd2VyZnVsIHRlY2huaXF1ZXMgZm9yIGFuYWx5emluZyBhbmQgZXhwbG9yaW5nIGEgZGF0YXNldA0KDQp3ZSdsbCBkbyB0aGVzZSBieSB0YWtpbmcgYSBjbG9zZSBsb29rIGF0IHNvbWUgaW50ZXJlc3RpbmcgZGF0YTogQWlyYm5iIGxpc3RpbmdzIGZvciBOZXcgWW9yayBDaXR5IGluIE1heSAyMDE3Lg0KDQpJdCdzIE9rYXkgaWYgeW91IGNhbid0IHN0YXJ0IHVzaW5nIHRoZXNlIHRlY2huaXF1ZXMgcmlnaHQgYXdheSBhZnRlciB0aGUgd29ya3Nob3AuIFRvIGJlIGNvbWZvcnRhYmxlIHdpdGggYSB0b29sIGxpa2UgUiB0YWtlcyBzb21lIHRpbWUgYW5kIHByYWN0aWNlLiBCdXQgd2UgZG8gaG9wZSB0aGF0IHlvdXIgZXhwb3N1cmUgdG9kYXkgdG8gdGhlc2UgdG9vbHMgYW5kIHRlY2huaXF1ZXMgd2lsbCBvcGVuIHlvdXIgZXllcyBhYm91dCBwb3NzaWJpbGl0aWVzIGFuZCBtb3RpdmF0ZSB5b3UgdG8ga2VlcCBsZWFybmluZy4NCg0KIyBSU3R1ZGlvDQoNClJTdHVkaW8gaXMgYSBmcmVlIHByb2dyYW0gdGhhdCBtYWtlcyB3cml0aW5nIFIgY29kZSBtdWNoIG1vcmUgZW5qb3lhYmxlIGFuZCBlZmZpY2llbnQuDQoNCkl0IGhhcyBmb3VyIG1haW4gcGFuZXMuIFRoaXMgb25lIGlzIHRoZSBjb2RlIGVkaXRvciAoYWxzbyBrbm93biBhcyBzY3JpcHQgZWRpdG9yIGFuZCBzb3VyY2UgZWRpdG9yKS4gVGhpcyBpcyB3aGVyZSB3ZSdsbCBzcGVuZCBtb3N0IG9mIG91ciB0aW1lLiBXZSdsbCBzYXkgbW9yZSBhYm91dCB0aGUgb3RoZXIgcGFuZXMgd2hlbiB0aGUgdGltZSBjb21lcy4gDQoNCiMgUiBOb3RlYm9vaw0KDQpCZWZvcmUgc3RhcnRpbmcgdGhlIGFuYWx5c2lzLCBsZXQncyB1bmRlcnN0YW5kIGhvdyB0aGlzIGRvY3VtZW50IHdvcmtzLiBJJ20gYXNzdW1pbmcgeW91J3JlIHJlYWRpbmcgdGhpcyBvbiBSU3R1ZGlvLiANCg0KVGhpcyBpcyBhbiBSIE5vdGVib29rLiBBbiBSIE5vdGVib29rIGNvbnRhaW5zIGNvbW1lbnRhcnkgaW50ZXJzcGVyc2VkIHdpdGggY29kZSBjaHVua3MuIFRoZSBjb2RlIGNodW5rcyBjYW4gYmUgcnVuIChleGVjdXRlZCkgaW5kZXBlbmRlbnRseSBhbmQgaW50ZXJhY3RpdmVseS4gVGhlIG91dHB1dCB3aWxsIGFwcGVhciBiZWxvdyB0aGUgY29kZSBjaHVuay4gUiBOb3RlYm9va3MgYXJlIGVhc3kgdG8gY29udmVydCB0byB3ZWxsLWZvcm1hdHRlZCBmaW5hbCBkb2N1bWVudHMgYXMgYSBwZGYgZmlsZSwgYSB3ZWJwYWdlLCBvciBhbiBNUyBXb3JkIGZpbGUuIFtNb3JlIGhlcmVdKGh0dHA6Ly9ybWFya2Rvd24ucnN0dWRpby5jb20vcl9ub3RlYm9va3MuaHRtbCkNCg0KQ2xpY2sgb24gdGhlIGBwcmV2aWV3YCBidXR0b24gYWJvdmUgdG8gZ2V0IGFuIGlkZWEuIA0KDQpXaGF0IHlvdSdyZSByZWFkaW5nIGlzIHRoZSBjb21tZW50YXJ5IGFuZCB3aGF0IHlvdSBzZWUgYmVsb3cgaXMgdGhlIGNvbnRhaW5lciBmb3IgYSBjb2RlIGNodW5rLg0KDQpgYGB7cn0NCiMgVGhlIGNvZGUgZ29lcyBoZXJlDQpgYGANCg0KVG8gcnVuIGEgY29kZSBjaHVuaywgY2xpY2sgb24gdGhlIGxpdHRsZSB0cmlhbmdsZSBhdCB0aGUgcmlnaHQgZWRnZSBvZiB0aGUgY29kZSBjaHVuay4gT3IsIHVzZSB0aGUga2V5Ym9hcmQgc2hvcnRjdXQgKipDVFJMICsgU0hJRlQgKyBFTlRFUioqLg0KDQoqRXhlcmNpc2U6IFJ1biB0aGUgY2h1bmsgYmVsb3cgYW5kIHNlZSB3aGF0IGhhcHBlbnMuKg0KYGBge3J9DQphIDwtIDUgKyA3DQpwcmludChhKQ0KYGBgDQoNCkFzIHlvdSBzZWUsIHRoZSBvdXRwdXQgYXBwZWFycyB1bmRlciB0aGUgY29kZS4gVGhpcyB3YXkgeW91IGNhbiBpbW1lZGlhdGVseSBzZWUgdGhlIHJlc3VsdCBvZiB0aGUgY29kZSB5b3Ugd3JpdGUgYXQgYWxsIHN0ZXBzIG9mIHRoZSBhbmFseXNpcy4NCg0KKERvIHlvdSBrbm93IHdoYXQncyBnb2luZyBvbiBpbiB0aGUgY29kZSBhYm92ZT8gV2UnbGwgdGFsayBhYm91dCB2YXJpYWJsZXMgYW5kIGFzc2lnbm1lbnRzIGxhdGVyLikNCg0KRmVlbCBmcmVlIHRvIGFkZCB5b3VyIGNvbW1lbnRhcnkgYW5kIGNvZGUgYW55d2hlcmUgaW4gdGhpcyBkb2N1bWVudC4gWW91IGNhbiBhbHdheXMgZG93bmxvYWQgdGhlIHVubW9kaWZpZWQgdmVyc2lvbiBmcm9tIFtoZXJlXShodHRwczovL2dpdGh1Yi5jb20vYXNpZm0vdGVjaC13b3Jrc2hvcHMvdHJlZS9tYXN0ZXIvRGF0YXNldHMpLiANCg0KVG8gd3JpdGUgeW91ciBvd24gY29kZSBjaHVuaywgbG9vayBmb3IgdGhlICoqaW5zZXJ0KiogYnV0dG9uIGFib3ZlIGFuZCB0aGVuIHNlbGVjdCAqKlIqKi4gT3IsIHVzZSB0aGUga2V5Ym9hcmQgc2hvcnRjdXQgKipDVFJMICsgQUxUICsgSSoqLiANCg0KKkV4ZXJjaXNlOiBQbGFjZSB0aGUgY3Vyc29yIGJlbG93IGFuZCBjcmVhdGUgYSBjb250YWluZXIgZm9yIHdyaXRpbmcgY29kZS4gTm93IHdyaXRlIHNvbWUgY29kZSBhbmQgcnVuIGl0LiBGb3IgZXhhbXBsZSwgZmluZCB0aGUgcmVzdWx0IG9mIGAzNTQzIC8gNTYyYC4qDQoNCg0KDQoNCiMgVGhlIGRhdGENCg0KVGhlIGRhdGFzZXQgY29udGFpbnMgYWxtb3N0IGFsbCB0aGUgbGlzdGluZ3MgaW4gTllDIGluIE1heSAyMDE3LlRoZSBkYXRhIGNhbWUgZnJvbSBbSW5zaWRlIEFpcmJuYl0oaHR0cDovL2luc2lkZWFpcmJuYi5jb20vZ2V0LXRoZS1kYXRhLmh0bWwpLg0KDQpJdCdzIGFsd2F5cyBhIGdvb2QgaWRlYSB0byBhcHByb2FjaCBhIG5ldyBkYXRhc2V0IHdpdGggYSBmZXcgYmFzaWMgcXVlc3Rpb25zLiBTb21lIGV4YW1wbGVzOg0KDQotIFdoYXQga2luZCBvZiBkYXRhIGlzIGhlcmU/IFdoYXQgYXJlIHRoZSB2YXJpYWJsZXM/IEhvdyBtYW55IG9ic2VydmF0aW9ucz8NCi0gV2hvIGNvbGxlY3RlZCB0aGVzZSBkYXRhPyBIb3c/IFdoeT8NCi0gSG93IGFjY3VyYXRlIGFyZSB0aGVzZSBkYXRhPw0KLSBIb3cgY29tcGxldGUgYXJlIHRoZXNlIGRhdGE/IEFyZSB0aGVyZSB0b28gbWFueSBtaXNzaW5nIHZhbHVlcz8NCi0gQXJlIHRoZXJlIGFub21hbGllcyB0aGF0IEkgbmVlZCB0byBiZSBhd2FyZSBvZj8NCi0gRG8gSSBoYXZlIHRoZSBsZWdhbCByaWdodHMgdG8gdXNlIHRoZXNlIGRhdGE/IFVuZGVyIHdoYXQgY29uZGl0aW9ucz8NCg0KW0hlcmUncyBzb21lIGJhY2tncm91bmQgaW5mb3JtYXRpb25dKGh0dHA6Ly9pbnNpZGVhaXJibmIuY29tL2JlaGluZC5odG1sKSB0aGF0IG1heSBhbnN3ZXIgc29tZSBvZiB0aGVzZSBxdWVzdGlvbnMuDQoNCkxldCdzIHRha2UgYSBsb29rIGF0IHRoZSBkYXRhLiBPcGVuIHRoZSBjc3YgZmlsZSBpbiBFeGNlbC4gQnJvd3NlIGFyb3VuZCBhIGJpdC4gQW55IG9ic2VydmF0aW9ucyBvciBxdWVzdGlvbnM/DQoNCipEaXNjdXNzOiBDYW4geW91IGV4cGxhaW4gd2hhdCBlYWNoIGNvbHVtbiBpcyBhYm91dD8qDQoNCipEaXNjdXNzOiBBcmUgdGhlcmUgYW55IGRhdGEgdGhhdCB5b3UnZCBsaWtlIHRvIGhhdmUgYnV0IG5vdCB0aGVyZT8qDQoNCipEaXNjdXNzOiBXaGF0J3MgdGhlIGZpcnN0IHRoaW5nIHlvdSdkIGxpa2UgdG8gZmluZCBvdXQgZnJvbSB0aGVzZSBkYXRhPyoNCg0KKkRpc2N1c3M6IFdoYXQgZG8geW91IHRoaW5rIGFib3V0IHRoZSBxdWFsaXR5IG9mIHRoZSBkYXRhPyBXaHk/Kg0KDQoNCg0KDQojIFRoZSBjb250ZXh0DQoNCkRhdGEgYW5hbHlzaXMgaGFwcGVucyB3aXRoaW4gYSBsYXJnZXIgY29udGV4dC4gVXN1YWxseSB0aGVyZSdzIGFuIG92ZXJhcmNoaW5nIGJ1c2luZXNzLCBwb2xpY3ksIG9yIHNjaWVudGlmaWMgcXVlc3Rpb24gdGhhdCBvbmUgd291bGQgbGlrZSB0byBhbnN3ZXIuIE9mdGVuIHRoYXQgcXVlc3Rpb24gaXMgbm90IHZlcnkgY2xlYXIuIFdoYXRldmVyIHRoZSBjYXNlLCB5b3UgbmVlZCB0byBhcHByb2FjaCB0aGUgZGF0YSB3aXRoIGN1cmlvc2l0eSBhYm91dCB0aGUgbGFyZ2VyIGNvbnRleHQuICANCg0KVGhlIG1vcmUgeW91IHVuZGVyc3RhbmQgdGhlIGNvbnRleHQgb2YgdGhlIGRhdGEsIHRoZSBiZXR0ZXIgd2lsbCBiZSB5b3VyIHF1ZXN0aW9ucyBhbmQgaHlwb3RoZXNlcyBndWlkaW5nIHlvdXIgYW5hbHlzaXMuDQoNCkZvciBvdXIgZGF0YXNldCwgd2Ugc2hvdWxkIGhhdmUgYSBnb29kIHVuZGVyc3RhbmRpbmcgb2YgdGhlIGJ1c2luZXNzIG1vZGVsIG9mIEFpcmJuYiBhcyB3ZWxsIGFzIHRoZSBlY29ub215IGFuZCBnZW9ncmFwaHkgb2YgTllDLiANCg0KKkRpc2N1c3M6IElzIHRoZXJlIGFueXRoaW5nIGVsc2Ugd2Ugc2hvdWxkIGtub3cgYWJvdXQ/Kg0KDQojIyBBaXJibmINCg0KKkRpc2N1c3M6IEhvdyBtdWNoIGRvIHdlIG5lZWQgdG8ga25vdyBhYm91dCBBaXJibmIgYW5kIGl0cyBidXNpbmVzcz8gSXMgbXkgcGVyc29uYWwgZXhwZXJpZW5jZSB3aXRoIEFpcmJuYiBlbm91Z2g/IFdoYXQgaWYgSSBkb24ndCBoYXZlIGFueSBwZXJzb25hbCBleHBlcmllbmNlPyoNCg0KW0hvdyBBaXJibmIgd29ya3NdKGh0dHBzOi8vd3d3Lndpa2l3YW5kLmNvbS9lbi9BaXJibmIjL0hvd19pdF93b3JrcykNCg0KW0V4YW1wbGUgYWlyYm5iIGxpc3RpbmddKGh0dHBzOi8vd3d3LmFpcmJuYi5jb20vcm9vbXMvMjE2ODU5ND9zPWZyZmZJWFdTKQ0KDQojIyBOZXcgWW9yayBDaXR5DQoNCldlIGNhbiBzdGFydCB3aXRoIGEgbWFwIG9mIHRoZSBjaXR5Lg0KDQohW2ltZ10oaHR0cHM6Ly9pLmltZ3VyLmNvbS9zZTdmVFlULnBuZykNCg0KVGhlIG1hcCBzaG93cyB0aGUgcmVsYXRpdmUgc2l6ZSBhbmQgbG9jYXRpb24gb2YgdGhlIGZpdmUgYm9yb3VnaHMgb2YgTllDLiANCg0KKkRpc2N1c3M6IFdoYXQgZWxzZSBkbyB3ZSBrbm93IGFib3V0IHRoZXNlIGJvcm91Z2hzPyBXaGF0IGFib3V0IHRoZSBuZWlnaGJvcmhvb2RzIHdpdGhpbiB0aGUgYm9yb3VnaHM/Kg0KDQoNCg0KIyBBbmFseXNpcw0KDQojIyBJbnN0YWxsIGFuZCBsb2FkIGxpYnJhcmllcw0KDQpXZSdsbCBmaXJzdCBpbnN0YWxsIGEgZmV3IGxpYnJhcmllcyB0aGF0IHdlJ2xsIG5lZWQgYXQgZGlmZmVyZW50IHN0YWdlcyBvZiB0aGUgYW5hbHlzaXMuIA0KDQpgYGB7cn0NCiMgRm9yIGxvYWRpbmcgZGF0YQ0KaW5zdGFsbC5wYWNrYWdlcygicmVhZHIiKQ0KIyBGb3IgZGF0YSBtYW5pcHVsYXRpb24gKHdlJ2xsIHNwZW5kIG1vc3QgdGltZSB3aXRoIHRoaXMgb25lKQ0KaW5zdGFsbC5wYWNrYWdlcygiZHBseXIiKQ0KIyBGb3IgdmlzdWFsaXphdGlvbg0KaW5zdGFsbC5wYWNrYWdlcygiZ2dwbG90MiIpDQojIEZvciBzdHJpbmcgbWFuaXB1bGF0aW9uDQppbnN0YWxsLnBhY2thZ2VzKCJzdHJpbmdyIikNCiMgVG8gd29yayB3aXRoIGRhdGUgYW5kIHRpbWUNCmluc3RhbGwucGFja2FnZXMoImx1YnJpZGF0ZSIpDQpgYGANCg0KQW5kIHRoZW4gd2UgbG9hZCB0aGUgbGlicmFyaWVzIGZvciBvdXIgY3VycmVudCBSIHNlc3Npb24uIEFmdGVyIHRoZXNlIGxpYnJhcmllcyBhcmUgc3VjY2Vzc2Z1bGx5IGxvYWRlZCwgYWxsIHRoZSBmdW5jdGlvbnMgaW4gdGhlc2UgbGlicmFyaWVzIHdpbGwgYmUgYXZhaWxhYmxlIGZvciBvdXIgdXNlLg0KDQpgYGB7cn0NCmxpYnJhcnkocmVhZHIpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShzdHJpbmdyKQ0KbGlicmFyeShsdWJyaWRhdGUpDQpgYGANCg0KIyMgTG9hZCBhbmQgcHJlcGFyZSBkYXRhDQoNCldlJ2xsIHVzZSB0aGUgZnVuY3Rpb24gYHJlYWRfY3N2YCwgd2hpY2ggY29tZXMgZnJvbSB0aGUgbGlicmFyeSBgcmVhZHJgLCB0byBsb2FkIGRhdGEgZnJvbSBmaWxlIGFuZCBjb252ZXJ0IHRoZSBkYXRhIGludG8gYSBkYXRhZnJhbWUuIEEgZGF0YWZyYW1lIGlzIGEgdGFidWxhciBkYXRhIHN0cnVjdHVyZSwgd2l0aCBjb2x1bW5zIGFzIHZhcmlhYmxlcyBhbmQgcm93cyBhcyBvYnNlcnZhdGlvbnMuIEluIHRoaXMgY2FzZSwgZXhhY3RseSBhcyBpdCBpcyBpbiB0aGUgY3N2IGZpbGUuDQoNCipEaXNjdXNzOiBXaGF0J2EgY3N2IGZpbGU/IFdoYXQgb3RoZXIgZm9ybWF0cyBhcmUgb3V0IHRoZXJlIGZvciBzdG9yaW5nIGRhdGE/IEhvdyBjYW4gd2UgbG9hZCBkYXRhIGZyb20gYW4gdW5mYW1pbGlhciBmb3JtYXQ/Kg0KDQpgYGB7cn0NCmRmIDwtIHJlYWRfY3N2KCJhaXJibmJfbmV3eW9yay5jc3YiKQ0KYGBgDQoNCkZpcnN0IHRoaW5nIHRvIGNoZWNrIGlmIHRoZSBkYXRhIGhhdmUgYmVlbiBzdWNjZXNzdWxseSBsb2FkZWQgYW5kIGFzc2lnbmVkIHRvIHRoZSB2YXJpYWJsZSAoaGVyZSBgZGZgKS4gV2UgZG9uJ3Qgc2VlIGFueSBlcnJvciDigJQgd2hpY2ggaXMgYSBnb29kIHNpZ24uIFdlIGNhbiBhbHNvIHNlZSB0aGUgdmFyaWFibGUgYGRmYCBpbiB0aGUgZW52aXJvbm1lbnQgcGFuZSAodXN1YWxseSB0byB0aGUgcmlnaHQgb2YgdGhpcyBjb2RlIGVkaXRvciBwYW5lIOKAkyBjbGljayBvbiAiRW52aXJvbm1lbnQiIGlmIGhpZGRlbikuDQoNCk5leHQgdGhpbmcgdG8gY2hlY2sgaXMgd2hldGhlciByZWFkX2NzdiB3YXMgYWJsZSB0byBjb3JyZWN0bHkgaW5mZXIgdGhlIGRhdGEgdHlwZSBvZiBlYWNoIHZhcmlhYmxlLg0KDQpUaGUgZnVuY3Rpb24gYGdsaW1wc2VgIHdpbGwgZ2l2ZSB1cyBhIGJldHRlciBnbGltcHNlLg0KYGBge3J9DQpnbGltcHNlKGRmKQ0KYGBgDQoNCldoYXQgeW91IHNlZSB3aXRoaW4gdGhlIGFuZ3VsYXIgYnJhY2tldHMgYDwuLi4+YCBuZXh0IHRvIGVhY2ggY29sdW1uIG5hbWUgaXMgdGhlIGRhdGEgdHlwZSBvZiB0aGF0IHZhcmlhYmxlLiANCg0KQnV0IHdoYXQgYXJlIGRhdGEgdHlwZXM/IEFuZCwgd2h5IGFyZSB0aGV5IGltcG9ydGFudD8gV2hhdCBleGFjdGx5IGlzIGEgdmFyaWFibGUgaW4gcHJvZ3JhbW1pbmc/IFdoYXQgaXMgYSBmdW5jdGlvbj8NCg0KTGV0J3MgdGFrZSBhIGRldG91ci4NCg0KIyBEZXRvdXI6IHZhcmlhYmxlcywgZGF0YSB0eXBlcywgYW5kIGZ1bmN0aW9ucyBpbiBSDQoNCiMjIFZhcmlhYmxlcyAocHJvZ3JhbW1pbmcpDQoNCkluIHByb2dyYW1taW5nLCBhIHZhcmlhYmxlIHN0b3JlcyBzb21lIHZhbHVlIChub3QgdG8gYmUgY29uZnVzZWQgd2l0aCB3aGF0IHdlIGNhbGwgdmFyaWFibGUgaW4gc3RhdGlzdGljcykuIE91ciBjb2RlIGNhbiB0aGVuIHJlZmVyZW5jZSB0aGUgbmFtZSBvZiB0aGUgdmFyaWFibGUgZm9yIGRpZmZlcmVudCBwdXJwb3Nlcy4gDQoNCldlIHVzZSB0aGUgc3ltYm9sIGA8LWAgKGxlc3MgdGhhbiBzaWduIGZvbGxvd2VkIGJ5IGh5cGhlbikgdG8gYXNzaWduIHZhbHVlIHRvIGEgdmFyaWFibGUgbmFtZS4gRm9yIGV4YW1wbGUsIGluIHRoZSBjb2RlIGJlbG93LCBgZm9vYCBhbmQgYGJhcmAgYXJlIHZhcmlhYmxlIG5hbWVzLiBXZSBhc3NpZ25lZCB0aGUgdmFsdWVzIGA0M2AgYW5kIGAia2l0dGVuImAgdG8gYGZvb2AgYW5kIGBiYXJgIHJlc3BlY3RpdmVseS4gWW91IGNhbiByZWFkIHRoZW0gYXMgYGZvb2AgZ2V0cyBgNDNgIGFuZCBgYmFyYCBnZXRzIGAia2l0dGVuImAuDQoNCmBgYHtyfQ0KZm9vIDwtIDQzDQpiYXIgPC0gImtpdHRlbiINCnByaW50KGZvbyAqIDEwKQ0KYGBgDQoNCkltcG9ydGFudDogSSd2ZSBiZWVuIHVzaW5nIHRoZSB3b3JkICp2YXJpYWJsZSogdG8gbWVhbiB0d28gZGlmZmVyZW50IHRoaW5ncy4gQSB2YXJpYWJsZSBpbiBwcm9ncmFtbWluZyBpcyB3aGF0IEkgZGVzY3JpYmVkIGFib3ZlLiBBIHZhcmlhYmxlIGluIHN0YXRpc3RpY3MgaXMgYW4gYXR0cmlidXRlIG9mIHNvbWV0aGluZywgd2hhdGV2ZXIgdGhlIGRhdGEgaXMgYWJvdXQuIEZvciBleGFtcGxlLCBpZiB0aGUgZGF0YSBpcyBhYm91dCBwZW9wbGUsIHNvbWUgdmFyaWFibGVzIGNvdWxkIGJlIGFnZSwgc2V4LCBpbmNvbWUsIGFkZHJlc3MuIFRoZSBuYW1lcyBvZiB0aGUgY29sdW1ucyBvZiBhIGRhdGFmcmFtZSBhcmUgdmFyaWFibGVzIGluIHRoZSBzdGF0aXN0aWNhbCBzZW5zZS4gVXN1YWxseSwgdGhlIGNvbnRleHQgd291bGQgY2xhcmlmeSB3aGljaCBtZWFuaW5nIGlzIGludGVuZGVkLg0KDQpUaXA6IEdpdmUgc2hvcnQgZGVzY3JpcHRpdmUgbmFtZXMgdG8geW91ciB2YXJpYWJsZXMuIFRoaXMgd2lsbCBtYWtlIHRoZSBjb2RlIGVhc3kgdG8gZm9sbG93Lg0KDQoqRXhlcmNpc2U6IGEgcm9vbSBpcyAxMSB5YXJkcyBsb25nIGFuZCA3LjUgeWFyZHMgd2lkZS4gQXNzaWduIHRoZXNlIHZhbHVlcyB0byBgd2lkdGhgIGFuZCBgbGVuZ3RoYCB2YXJpYWJsZXMgYW5kIHRoZW4gY2FsY3VsYXRlIHRoZSBgYXJlYWAgb2YgdGhlIHJvb20uIFlvdSBjYW4sIG9mIGNvdXJzZSwgZ2l2ZSBkaWZmZXJlbnQgbmFtZXMgdG8gdGhlc2UgdmFyaWFibGVzIGlmIHlvdSB3aXNoLioNCg0KYGBge3J9DQoNCmBgYA0KDQojIyBEYXRhIHR5cGVzDQoNClRvIHVuZGVyc3RhbmQgaG93IFIgc3RvcmVzIGFuZCBoYW5kbGVzIGluZm9ybWF0aW9uLCB3ZSBuZWVkIHRvIGtub3cgYSBsaXR0bGUgYWJvdXQgZGF0YSB0eXBlcy4NCg0KQnV0LCBmaXJzdCwgdmVjdG9yOiBvbmUgb2YgdGhlIGtleSBjb25jZXB0cyBpbiBSLiBUaGluayBvZiBhIHZlY3RvciBhcyBzaW1wbHkgYSBzZXF1ZW5jZSBvZiBkYXRhLiBGb3IgZXhhbXBsZSwgYSBjb2x1bW4gaW4gYSBkYXRhIHRhYmxlIGlzIGEgdmVjdG9yLiBUaGlzIGlzIGhvdyB5b3UgY3JlYXRlIGEgdmVjdG9yOiBgYyhlbGVtZW50MSwgZWxlbWVudDIsIGVsZW1lbnQzLCAuLi4pYA0KDQpgYGB7cn0NCnZlYyA8LSBjKDMsIDUsIDEwLCAyMCkNCnByaW50KHZlYykNCmBgYA0KDQpBIHZlY3RvciBjYW4gY29udGFpbiBmb3VyIG1haW4gdHlwZXMgb2YgZGF0YToNCg0KLSBJbnRlZ2VyOiBzdWNoIGFzIDIsIDU0MywgOTAuDQotIERvdWJsZTogbnVtYmVycyBvdGhlciB0aGFuIGludGVnZXJzLCBzdWNoIGFzIDQuNTYsIDEvMzMuIERvdWJsZXMgYXJlIGFsd2F5cyBhcHByb3hpbWF0aW9ucy4NCi0gQ2hhcmFjdGVyOiBhIHNlcXVlbmNlIG9mIGxldHRlcnMsIG51bWJlcnMsIHB1bmN0dWF0aW9ucywgZXRjLiwgc3VjaCBhcyAicmFpbmJvdyIsICIzNDI2NSIuIENoYXJhY3RlciBkYXRhIGFyZSBhbHdheXMgd3JpdHRlbiB3aXRoaW4gcXVvdGVzLCBlaXRoZXIgc2luZ2xlIG9yIGRvdWJsZSAod2UnbGwgYWx3YXlzIHVzZSBkb3VibGUgZm9yIGNvbnNpc3RlbmN5KS4NCi0gTG9naWNhbDogZGF0YSB0aGF0IGNhbiB0YWtlIG9uIG9uZSBvZiBvbmx5IHR3byB2YWx1ZXMg4oCUIFRSVUUgb3IgRkFMU0UuDQoNCipEaXNjdXNzOiBXaHkgaXMgIjM0MjY1IiBvZiBjaGFyYWN0ZXIgdHlwZT8gV2hhdCBpcyBhIHJlYWwgd29ybGQgZXhhbXBsZSBvZiB0aGlzIGtpbmQgb2YgZGF0YT8qDQoNClRoZXJlIGFyZSB0d28gb3RoZXIgaW1wb3J0YW50IHdheXMgdG8gc3RvcmUgZGF0YTogDQotIGBmYWN0b3JgLCBmb3IgY2F0ZWdvcmljYWwgdmFyaWFibGVzIChoZXJlICJ2YXJpYWJsZSIgaW4gc3RhdGlzdGljYWwgc2Vuc2UpLiBBIGNhdGVnb3JpY2FsIHZhcmlhYmxlIGlzIG9uZSB0aGF0IGNhbiB0YWtlIG9uIGEgbGltaXRlZCBmaXhlZCBudW1iZXIgb2YgdmFsdWVzLg0KLSBgZGF0ZS10aW1lYCBvciBqdXN0IGBkYXRlYA0KDQoNCiMjIEZ1bmN0aW9ucw0KDQpUaGUgY29uY2VwdCBvZiBhIGZ1bmN0aW9uIGlzIGV4YWN0bHkgdGhlIHNhbWUgYXMgaW4gRXhjZWwuIFlvdSBnaXZlIHNvbWUgdmFsdWVzIChjYWxsZWQgcGFyYW1ldGVycykgdG8gYSBmdW5jdGlvbi4gVGhlIGZ1bmN0aW9uIGRvZXMgc29tZXRoaW5nIHdpdGggdGhlIHZhbHVlcyBhbmQgcmV0dXJucyBhIG5ldyB2YWx1ZS4NCg0KSGVyZSdzIGEgc2ltcGxlIGZ1bmN0aW9uIHRoYXQgd2UgbmFtZWQgYGFkZDEwYC4gQWxsIGl0IGRvZXMgaXMgYWRkcyAxMCB0byBhbnkgbnVtYmVyIGdpdmVuIHRvIGl0Lg0KDQpgYGB7cn0NCmFkZDEwIDwtICBmdW5jdGlvbihudW0pew0KICBuZXdfdmFsdWUgPC0gIG51bSArIDEwDQogIHJldHVybiAobmV3X3ZhbHVlKQ0KfQ0KYGBgDQoNCkFuZCwgdGhlbiwgd2UgY2FuIHVzZSB0aGlzIGZ1bmN0aW9uIHdoZW5ldmVyIHdlIG5lZWQgdG8gYWRkIDEwIHRvIGEgbnVtYmVyIChPZiBjb3Vyc2UsIHdlIHdvbid0LiBUaGVyZSBhcmUgZWFzaWVyIHdheXMgdG8gYWRkIGEgbnVtYmVyKS4NCg0KYGBge3J9DQojIEFkZCAxMCB0byAzNQ0KYWRkMTAoMzUpDQpgYGANCg0KVG9kYXksIHdlIHdvbid0IHdyaXRlIGFueSBmdW5jdGlvbiwgUmF0aGVyLCB3ZSdsbCB1c2UgZnVuY3Rpb25zIHdyaXR0ZW4gYnkgb3RoZXJzLiBIZXJlJ3MgYW4gZXhhbXBsZTogdGhlIGBtZWFuYCBmdW5jdGlvbiwgd2hpY2ggY29tZXMgcHJlbG9hZGVkIGluIFIuDQoNCmBgYHtyfQ0KIyBGaXJzdCBsZXQncyBjcmVhdGUgYSB2ZWN0b3INCnZlYzIgPC0gYygyMywgNTMsIDExLCAzNCwgODcsIDEwMCwgNSwgMTIsIDY2LCA5LCA4NywgMTEwLCAyMCwgMzMsIDU0LCA0MywgNzYpDQojIExldCdzIGNhbGN1bGF0ZSB0aGUgbWVhbiBvZiB0aGUgdmVjdG9yDQpjYWxjdWxhdGVkX21lYW4gPC0gbWVhbih2ZWMyKQ0KIyBUaGVuIG91dHB1dCBpdCB3aXRoIHNvbWUgdGV4dCBkZXNjcmlwdGlvbg0KbWVzc2FnZSgiVGhlIG1lYW4gb2YgdGhlIHZlY3RvciB2ZWMyIGlzICIsIG1lYW4oY2FsY3VsYXRlZF9tZWFuKSkNCmBgYA0KKkV4ZXJjaXNlOiBjYWxjdWxhdGUgdGhlIHN1bSwgbWVkaWFuLCBhbmQgc3RhbmRhcmQgZGV2aWF0aW9uIChoaW50OiBzZCkgb2YgdGhlIHZlY3RvciB2ZWMyLg0KDQpgYGB7cn0NCg0KYGBgDQoNCk5vdyB0aGF0IHdlIHVuZGVyc3RhbmQgZGF0YSB0eXBlcywgbGV0J3MgbG9hZCBzb21lIG5ldyBkYXRhLg0KDQoqRXhlcmNpc2U6IFlvdSdsbCBzZWUgdGhlcmUncyBhbm90aGVyIGRhdGEgZmlsZSBpbiB0aGUgZm9sZGVyOiAidXNfYmFieW5hbWVzLmNzdiIuIExvYWQgdGhlIGRhdGEgZnJvbSB0aGlzIGZpbGUgdG8gYSBkYXRhZnJhbWUgKGhpbnQ6IHJlYWRfY3N2KS4gQXNzaWduIHRoZSBkYXRhZnJhbWUgdG8gYSB2YXJpYWJsZSBuYW1lZCBgZGZfYmFieW5hbWVzYC4gV2UnbGwgY29tZSBiYWNrIHRvIHRoaXMgZGF0YWZyYW1lIGxhdGVyLioNCg0KYGBge3J9DQoNCmBgYA0KDQoqRXhlcmNpc2U6IE5vdyB0YWtlIGEgbG9vayBhdCB0aGUgZGF0YWZyYW1lJ3MgZGF0YSB0eXBlcy4gKGhpbnQ6IGdsaW1wc2UpKg0KDQpgYGB7cn0NCg0KYGBgDQoNCiMgQmFjayB0byBhbmFseXNpcw0KDQojIyBGaXggZGF0YSB0eXBlcw0KDQpMZXQncyB0YWtlIGFub3RoZXIgbG9vayBhdCB0aGUgZGF0YSB0eXBlcyBvZiBvdXIgZGF0YWZyYW1lLg0KDQpgYGB7cn0NCmdsaW1wc2UoZGYpDQpgYGANCg0KDQpTb21lIG9mIHRoZSBkYXRhIHR5cGVzIGRvbid0IGxvb2sgcmlnaHQuIExldCdzIGNvcnJlY3QgdGhlbS4NCg0KKkRpc2N1c3M6IHdoeSBpcyBpdCBpbXBvcnRhbnQgdG8gaGF2ZSB0aGUgY29ycmVjdCBkYXRhIHR5cGU/Kg0KDQpCdXQsIGZpcnN0LCBsZXQncyBsZWFybiBob3cgdG8gcmVmZXJlbmNlIGEgY29sdW1uIGluIGEgZGF0YWZyYW1lOiB0aGUgbmFtZSBvZiB0aGUgZGF0YWZyYW1lIGZvbGxvd2VkIGJ5IGAkYCBhbmQgdGhlbiB0aGUgY29sdW1uIG5hbWUuIEZvciBleGFtcGxlOiBgZGYkY2FuY2VsbGF0aW9uX3BvbGljeWAgb3IgYGRmX2JhYnluYW1lcyRHZW5kZXJgLg0KDQpUbyBjaGFuZ2UgZGF0YSB0eXBlLCB3ZSBhcHBseSB0aGUgcmVsZXZhbnQgZnVuY3Rpb24sIHN1Y2ggYGFzLmNoYXJhY3RlcmAgb3IgYGFzLmZhY3RvcmAsIHRvIGEgY29sdW1uIGFuZCB0aGVuIGFzc2lnbiB0aGUgb3V0cHV0IG9mIHRoZSBmdW5jdGlvbiAodmFsdWVzIHdpdGggY2hhbmdlZCBkYXRhIHR5cGUpIHRvIHRoZSBzYW1lIGNvbHVtbi4gDQoNCmBgYHtyfQ0KZGYkaG9zdF9pZCA8LSBhcy5jaGFyYWN0ZXIoZGYkaG9zdF9pZCkNCmRmJGhvc3RfcmVzcG9uc2VfcmF0ZSA8LSBhcy5kb3VibGUoZGYkaG9zdF9yZXNwb25zZV9yYXRlKQ0KZGYkcHJvcGVydHlfdHlwZSA8LSBhcy5mYWN0b3IoZGYkcHJvcGVydHlfdHlwZSkNCmRmJGhvc3Rfc2luY2UgPC0gbWR5KGRmJGhvc3Rfc2luY2UpDQpgYGANCg0KKkV4ZXJjaXNlOiBDaGFuZ2UgdGhlIGRhdGEgdHlwZSBvZiBhbnkgb3RoZXIgdmFyaWFibGVzIHlvdSB0aGluayBuZWNlc3NhcnkuKg0KYGBge3J9DQoNCmBgYA0KDQojIyBNaXNzaW5nIHZhbHVlcw0KDQpTb21lIHZhbHVlcyB3aWxsIGFsbW9zdCBpbmV2aXRhYmx5IGJlIG1pc3NpbmcgaW4gYSBtZWRpdW0gdG8gbGFyZ2UgZGF0YXNldC4gVGhlcmUgYXJlIGEgZmV3IG9wdGlvbnMgZm9yIGRlYWxpbmcgd2l0aCBtaXNzaW5nIHZhbHVlcy4NCg0KLSBXZSBjYW4gZHJvcCBhbGwgcm93cyB0aGF0IGhhdmUgb25lIG9yIG1vcmUgbWlzc2luZyB2YWx1ZXMuDQotIERyb3Agcm93cyB0aGF0IGhhdmUgbWlzc2luZyB2YWx1ZXMgaW4gcGFydGljdWxhciBjb2x1bW5zLg0KLSBSZXBsYWNlIHRoZSBtaXNzaW5nIHZhbHVlcyB3aXRoIHNvbWUgb3RoZXIgdmFsdWUuDQotIERvIG5vdGhpbmcsIGJ1dCBkbyByZW1lbWJlciB0byB0YWtlIGNhcmUgb2YgdGhlbSB3aGVuIHJ1bm5pbmcgYXJpdGhtZXRpYyBvcGVyYXRpb25zLiAoZXhwbGFpbmVkIGxhdGVyKQ0KDQpJbiBvdXIgY2FzZSwgd2UnbGwgZ28gd2l0aCB0aGUgbGFzdCBvcHRpb24uDQoNCipEaXNjdXNzOiBJcyB0aGUgbGFzdCBvcHRpb24gdGhlIGJlc3QgZm9yIG91ciBkYXRhc2V0PyBXaGF0IHdvdWxkIGJlIGEgYmV0dGVyIGFsdGVybmF0aXZlPyoNCg0KIyMgRGF0YSBtYW5pcGx1YXRpb24gYW5kIGV4cGxvcmF0aW9uDQoNCkZpbmFsbHksIHdlJ3JlIHJlYWR5IHRvIGRpdmUgaW50byB0aGUgZGF0YS4gV2UnbGwgdXNlIHNpeCBmdW5jdGlvbnMg4oCUIHRoaW5rIG9mIHRoZW0gYXMgc2l4IHZlcmJzIOKAlCB0byBzbGljZSBhbmQgZGljZSB0aGUgZGF0YSBpbiBhbGwga2luZHMgb2Ygd2F5cy4gVGhlc2UgZnVuY3Rpb25zIGNvbWUgZnJvbSB0aGUgZHBseXIgbGlicmFyeS4gVGhlc2UgYXJlOg0KDQotIGZpbHRlcg0KLSBzZWxlY3QNCi0gbXV0YXRlDQotIGFycmFuZ2UNCi0gc3VtbWFyaXplDQotIGdyb3VwX2J5DQoNCldlJ2xsIGZpcnN0IGxlYXJuIHRoZXNlIGZ1bmN0aW9ucyBvbmUgYnkgb25lIGFuZCBsYXRlciB3ZSdsbCBsZWFybiBob3cgdG8gY29tYmluZSB0aGVtIGZvciBwb3dlcmZ1bCBhbmFseXNpcy4NCg0KRm9yIGVhY2ggb2YgdGhlc2UgZnVuY3Rpb25zLCB3ZSBwcm92aWRlIHRoZSBuYW1lIG9mIHRoZSBkYXRhZnJhbWUgYXMgdGhlIGZpcnN0IHBhcmFtZXRlci4gVGhlIHN1YnNlcXVlbnQgcGFyYW1ldGVycyBpbmZvcm0gd2hhdCB0aGUgZnVuY3Rpb24gaXMgdG8gZG8gd2l0aCB0aGUgZGF0YWZyYW1lLg0KDQojIyMgZmlsdGVyDQoNCllvdSB1c2UgYGZpbHRlcmAsIHdoZW4geW91IHdhbnQgYSBzdWJzZXQgb2YgdGhlIHJvd3MgYmFzZWQgb24gb25lIG9yIG1vcmUgY29uZGl0aW9ucy4gVGhlIHJldHVybmVkIHJvd3Mgd2lsbCBiZSB0aG9zZSB0aGF0IG1lZXQgdGhlIGNvbmRpdGlvbihzKS4NCg0KVGhlIHN5bnRheCBpczogYGZpbHRlcihuYW1lX29mX2RhdGFmcmFtZSwgY29uZGl0aW9uMSwgY29uZGl0aW9uMiwgLi4uKWANCg0KVXNlIHRoZXNlICpsb2dpY2FsIG9wZXJhdG9ycyogdG8gY3JlYXRlIGNvbmRpdGlvbnM6DQogIA0KICA8CSAgICAgIGxlc3MgdGhhbg0KICA8PQkgICAgbGVzcyB0aGFuIG9yIGVxdWFsIHRvDQogID4JICAgICAgZ3JlYXRlciB0aGFuDQogID49CSAgICBncmVhdGVyIHRoYW4gb3IgZXF1YWwgdG8NCiAgPT0JICAgIGV4YWN0bHkgZXF1YWwgdG8NCiAgIT0JICAgIG5vdCBlcXVhbCB0bw0KICAheAkgICAgTm90IHgNCiAgeCB8IHkJICB4IE9SIHkNCiAgeCAmIHkJICB4IEFORCB5DQoNCg0KDQpFeGFtcGxlOiBSZXR1cm4gYSBkYXRhZnJhbWUgd2l0aCBvbmx5IHRob3NlIHJvd3Mgd2hlcmUgdGhlIG5laWdoYm91cmhvb2QgaXMgQ2hlbHNlYS4NCmBgYHtyfQ0KZmlsdGVyKGRmLCBuZWlnaGJvdXJob29kID09ICJDaGVsc2VhIikNCmBgYA0KDQpFeGFtcGxlOiBSZXR1cm4gYSBkYXRhZnJhbWUgd2l0aCBvbmx5IHRob3NlIHJvd3Mgd2hlcmUgdGhlIHByaWNlIGlzIG1vcmUgdGhhbiA1MDAwLg0KYGBge3J9DQpmaWx0ZXIoZGYsIHByaWNlID4gODAwMCkNCmBgYA0KDQpZb3UgY2FuIGNvbWJpbmUgbXVsdGlwbGUgY29uZGl0aW9ucy4NCg0KRXhhbXBsZTogUmV0dXJuIGEgZGF0YWZyYW1lIHdpdGggcm93cyB3aGVyZSBib3JvdWdoIGlzIE1hbmhhdHRhbiBhbmQgcHJpY2UgaXMgbGVzcyB0aGFuIDUwLg0KYGBge3J9DQpmaWx0ZXIoZGYsIGJvcm91Z2ggPT0gIk1hbmhhdHRhbiIsIHByaWNlIDwgMjApDQpgYGANCg0KRXhhbXBsZTogUmV0dXJuIGEgZGF0YWZyYW1lIHdpdGggcm93cyB3aGVyZSBjYW5jZWxsYXRpb25fcG9saWN5IGlzIGZsZXhpYmxlICpvciogYWNjb21tb2RhdGVzIG1vcmUgdGhhbiAyLg0KYGBge3J9DQpmaWx0ZXIoZGYsIGNhbmNlbGxhdGlvbl9wb2xpY3kgPT0gImZsZXhpYmxlIiB8IGFjY29tbW9kYXRlcyA+IDIgKQ0KYGBgDQoNCkV4YW1wbGU6IFJldHVybiBhIGRhdGFmcmFtZSB3aXRoIHJvd3Mgd2hlcmUgbnVtYmVyX29mX3Jldmlld3MgaXMgKm5vdCogMA0KYGBge3J9DQpmaWx0ZXIoZGYsIG51bWJlcl9vZl9yZXZpZXdzICE9IDApDQpgYGANCg0KDQpOb3RlOiB3aGVuIG11bHRpcGxlIGNvbmRpdGlvbnMgYXJlIHNlcGFyYXRlZCBieSBjb21tYXMgYXJlIGFzc3VtZWQgdG8gaGF2ZSAqYW5kKiBsb2dpY2FsIG9wZXJhdG9yLiBVc2luZyBgJmAgaW5zdGVhZCB3b3VsZCBiZSB0aGUgc2FtZSB0aGluZy4gRm9yICpvciogYW5kIG90aGVyIGxvZ2ljYWwgb3BlcmF0b3JzLCB3ZSBoYXZlIHRvIGV4cGxpY2l0bHkgdXNlIHRoZSBhcHByb3ByaWF0ZSBsb2dpY2FsIG9wZXJhdG9yIChlLmcuIGB8YCBmb3IgKm9yKikuDQoNCipFeGVyY2lzZTogUmV0dXJuIGEgZGF0YWZyYW1lIHdpdGggcm93cyB3aGVyZSBudW1iZXIgb2YgYmVkcm9vbXMgaXMgbm90IDEgYW5kIHByb3BlcnR5X3R5cGUgaXMgSG91c2UqDQpgYGB7cn0NCg0KYGBgDQoNCipFeGVyY2lzZTogUmV0dXJuIGEgZGF0YWZyYW1lIHdpdGggcm93cyB3aGVyZSBob3N0X3Jlc3BvbnNlX3RpbWUgaXMgd2l0aGluIGFuIGhvdXIgb3IgaG9zdF9yZXNwb25zZV9yYXRlIGlzIG1vcmUgdGhhbiA5MCUgb3IgY2FsZW5kYXIgd2FzIHVwZGF0ZWQgdG9kYXkqDQpgYGB7cn0NCg0KYGBgDQoNCiMjIyBzZWxlY3QNCg0KT3VyIHNlY29uZCB2ZXJiIGlzIGBzZWxlY3RgLCB3aGljaCBpcyB1c2VkIHRvIHJldHVybiBhIHN1YnNldCBvZiB0aGUgY29sdW1ucy4NCg0KVGhlIHN5bnRheCBpczogYHNlbGVjdChuYW1lX29mX2RhdGFmcmFtZSwgbmFtZV9vZl9jb2x1bW4xLCBuYW1lX29mX2NvbHVtbjIsIC4uLilgDQoNCkV4YW1wbGU6IFJldHVybiBhIGRhdGFmcmFtZSB3aXRoIG9ubHkgdGhlICpiZWRzKiBjb2x1bW4NCmBgYHtyfQ0Kc2VsZWN0KGRmLCBiZWRzKQ0KYGBgDQoNCkV4YW1wbGU6IFJldHVybiBhIGRhdGFmcmFtZSB3aXRoIGNvbHVtbnMgaG9zdF9yZXNwb25zZV90aW1lLCBjYW5jZWxsYXRpb25fcG9saWN5LCBhbmQgbmVpZ2hib3VyaG9vZA0KYGBge3J9DQpzZWxlY3QoZGYsIGhvc3RfcmVzcG9uc2VfdGltZSwgY2FuY2VsbGF0aW9uX3BvbGljeSwgbmVpZ2hib3VyaG9vZCkNCmBgYA0KDQoNCi0tLS0NCg0KSGVyZSBhcmUgc29tZSBleGNlbGxlbnQgcmVzb3VyY2VzIGlmIHlvdSB3YW50IHRvIGtlZXAgbGVhcm5pbmcgUjoNCg0KLSBodHRwOi8vdHJ5ci5jb2Rlc2Nob29sLmNvbQ0KLSBodHRwOi8vcjRkcy5oYWQuY28ubnovDQotIGh0dHA6Ly9zd2lybHN0YXRzLmNvbS8NCg0KDQo=